﻿using System;
using System.Collections.Generic;
using System.Text;

public static class GarageUI
{
    private const string k_Spaces = " ";
    private const int k_Zero = 0;
    private static GarageLogic m_GarageLogic = null;
    private static StringBuilder m_StringBuilder = null;
    private static string m_UserChosenOptionFromUI = string.Empty;
    private static int k_MinMenuValue = 1;
    private static int k_MaxMenuValue = 8;
    private static string k_QuitStr = "quit";

    private enum eMenuOption
    {
        AddNewVehicle = 0,
        ShowLicenseNumberOfAllVehiclesInGarage,
        ChangeHandledVehicleStatus,
        BlowUpHandeledVehicleWheels,
        FuelAVehicle,
        ChargeElectricVehicleBattery,
        ShowDetailsOfVehicle,
        Quit
    }

    public static void Start()
    {
        bool UserQuit = false;

        if (m_StringBuilder == null)
        {
            m_StringBuilder = new StringBuilder();
        }

        if (m_GarageLogic == null)
        {
            m_GarageLogic = new GarageLogic();
        }

        while (UserQuit == false)
        {
            try
            {
                System.Console.Clear();
                startMenu();
            }
            catch (ArgumentException e)
            {
                printException(e);
            }
            catch (FormatException e)
            {
                printException(e);
            }
            catch (ValueOutOfRangeException e)
            {
                printException(e);
            }
            catch (Exception e)
            {
                if (e.Message.Equals(k_QuitStr))
                {
                    UserQuit = true;
                }
            }
        }

        printQuitMessage();
    }

    private static void printQuitMessage()
    {
        System.Console.Clear();
        System.Console.WriteLine("Thank you for using our garage! :)");
        System.Threading.Thread.Sleep(2500);
    }

    private static void startMenu()
    {
        System.Console.Clear();
        printStartMenu();
        System.Console.WriteLine("number of vehicles in garage: " + m_GarageLogic.VehiclesInGarage.Count);
        eMenuOption UserChosenMenuOption = getUserChosenOption();

        switch (UserChosenMenuOption)
        {
            case eMenuOption.AddNewVehicle:
                {
                    addVehicleToGarageMenu();
                    break;
                }

            case eMenuOption.ShowLicenseNumberOfAllVehiclesInGarage:
                {
                    showLicenseNumberOfAllVehiclesInGarageMenu();
                    break;
                }

            case eMenuOption.ChangeHandledVehicleStatus:
                {
                    ChangeHandledVehicleStatus();
                    break;
                }

            case eMenuOption.BlowUpHandeledVehicleWheels:
                {
                    blowUpHandeledVehicleWheelsMenu();
                    break;
                }

            case eMenuOption.FuelAVehicle:
                {
                    fuelAVehicleMenu();
                    break;
                }

            case eMenuOption.ChargeElectricVehicleBattery:
                {
                    ChargeElectricVehicleBatteryMenu();
                    break;
                }

            case eMenuOption.ShowDetailsOfVehicle:
                {
                    showDetailsOfVehicleMenu();
                    break;
                }

            case eMenuOption.Quit:
                {
                    throw new Exception(k_QuitStr);
                }

            default:
                {
                    throw new ValueOutOfRangeException("Invalid option selected from menu", k_MinMenuValue, k_MaxMenuValue);
                }
        }
    }

    private static void printStartMenu()
    {
        printHeader();
        clearstringBuilder();
        appendStartMenu();
        System.Console.WriteLine(m_StringBuilder);
    }

    private static void appendStartMenu()
    {
       m_StringBuilder.Append(k_Spaces);
        m_StringBuilder.Append("Welcome to the garage");
        m_StringBuilder.Append(System.Environment.NewLine);
        m_StringBuilder.Append(k_Spaces);
        m_StringBuilder.Append("Please choose an option:");
        m_StringBuilder.Append(System.Environment.NewLine);
        m_StringBuilder.Append(k_Spaces);
        m_StringBuilder.Append("1. Add new vehicle to garage");
        m_StringBuilder.Append(System.Environment.NewLine);
        m_StringBuilder.Append(k_Spaces);
        m_StringBuilder.Append("2. Show license numbers of vehicles handled in garage");
        m_StringBuilder.Append(System.Environment.NewLine);
        m_StringBuilder.Append(k_Spaces);
        m_StringBuilder.Append("3. Change handled vehicle status");
        m_StringBuilder.Append(System.Environment.NewLine);
        m_StringBuilder.Append(k_Spaces);
        m_StringBuilder.Append("4. Blow up an handled vehicle wheels");
        m_StringBuilder.Append(System.Environment.NewLine);
        m_StringBuilder.Append(k_Spaces);
        m_StringBuilder.Append("5. Fuel a vehicle");
        m_StringBuilder.Append(System.Environment.NewLine);
        m_StringBuilder.Append(k_Spaces);
        m_StringBuilder.Append("6. Charge electric vehicle battery");
        m_StringBuilder.Append(System.Environment.NewLine);
        m_StringBuilder.Append(k_Spaces);
        m_StringBuilder.Append("7. Show details of a vehicle");
        m_StringBuilder.Append(System.Environment.NewLine);
        m_StringBuilder.Append(k_Spaces);
        m_StringBuilder.Append("8. Quit Garage");
        m_StringBuilder.Append(System.Environment.NewLine);
    }

    private static void clearstringBuilder()
    {
        m_StringBuilder.Remove(0, m_StringBuilder.Length);
    }

    private static void showDetailsOfVehicleMenu()
    {
        string VehicleLicenseNumber = getLicenseNumberFromUser();
        VehicleInGarage ResultVehicleInGarage = null;

        if (m_GarageLogic.IsVehicleInGarage(VehicleLicenseNumber, ref ResultVehicleInGarage))
        {
            System.Console.Clear();
            System.Console.WriteLine("This is the details for vehicle with license number {0} in garage:" + System.Environment.NewLine, VehicleLicenseNumber);
            System.Console.WriteLine(ResultVehicleInGarage.ToString());
            System.Threading.Thread.Sleep(2500);
        }
        else
        {
            System.Console.Clear();
            System.Console.WriteLine(@"Vehicle {0} not found ",  VehicleLicenseNumber);
            System.Threading.Thread.Sleep(2500);
        }
    }

    public static void ChargeElectricVehicleBatteryMenu()
    {
        VehicleInGarage ResultVehicleInGarage = null;
        float ChargeHours = 0;

        try
        {
            string VehicleLicenseNumber = getLicenseNumberFromUser();

            if (m_GarageLogic.IsVehicleInGarage(VehicleLicenseNumber, ref ResultVehicleInGarage))
            {
                System.Console.WriteLine(@"Please enter the desired hours to charge" + System.Environment.NewLine);
                string ChargeHoursStr = System.Console.ReadLine();
                ChargeHours = float.Parse(ChargeHoursStr);

                ElectricEngine CurrentElectricEngine = ResultVehicleInGarage.Vehicle.Engine as ElectricEngine;

                if (CurrentElectricEngine != null)
                {
                    CurrentElectricEngine.LoadBattery(ChargeHours);
                    System.Console.WriteLine("Charged car {0} with {1} hours of electric" + System.Environment.NewLine, VehicleLicenseNumber, ChargeHours);
                    System.Threading.Thread.Sleep(2000);
                }
                else
                {
                    throw new ArgumentException("Can't charge a fuel vehicle");
                }
            }
            else
            {
                throw new VehicleNotFoundException("Vehicle " + VehicleLicenseNumber + " not found");
            }
        }
        catch (VehicleNotFoundException e)
        {
            printException(e);
        }
        catch (ArgumentException e)
        {
            printException(e);
        }
        catch (FormatException e)
        {
            printException(e);
        }
        catch (ValueOutOfRangeException e)
        {
            printException(e);
        }
    }

    private static void fuelAVehicleMenu()
    {
        VehicleInGarage ResultVehicleInGarage = null;
        float FuelAmount = 0;

        try
        {
            string VehicleLicenseNumber = getLicenseNumberFromUser();

            if (m_GarageLogic.IsVehicleInGarage(VehicleLicenseNumber, ref ResultVehicleInGarage))
            {
                FuelEngine.eEngineFuelType FuelType = getChosenFuelType();

                System.Console.WriteLine(@"Please enter the desired amount of fuel" + System.Environment.NewLine);
                string FuelAmountStr = System.Console.ReadLine();
                FuelAmount = float.Parse(FuelAmountStr);

                FuelEngine CurrentFuelEngine = ResultVehicleInGarage.Vehicle.Engine as FuelEngine;

                if (CurrentFuelEngine != null)
                {
                    CurrentFuelEngine.LoadFuel(FuelType, FuelAmount);
                    System.Console.Clear();
                    System.Console.WriteLine("Loaded car {0} with fuel type {1} and fuel ammount {2}" + System.Environment.NewLine, VehicleLicenseNumber, FuelType.ToString(), FuelAmountStr);
                    System.Threading.Thread.Sleep(2000);
                }
                else
                {
                    throw new ArgumentException("Can't fuel an electric vehicle");
                }
            }
            else
            {
                throw new VehicleNotFoundException("Vehicle " + VehicleLicenseNumber + " not found");
            }
        }
        catch (VehicleNotFoundException e)
        {
            printException(e);
        }
        catch (ArgumentException e)
        {
            printException(e);
        }
        catch (FormatException e)
        {
            printException(e);
        }
        catch (ValueOutOfRangeException e)
        {
            printException(e);
        }
    }

    private static void blowUpHandeledVehicleWheelsMenu()
    {
        VehicleInGarage ResultVehicleInGarage = null;

        string VehicleLicenseNumber = getLicenseNumberFromUser();

        try
        {
            if (m_GarageLogic.IsVehicleInGarage(VehicleLicenseNumber, ref ResultVehicleInGarage))
            {
                ResultVehicleInGarage.Vehicle.BlowWheel();
                System.Console.Clear();
                System.Console.WriteLine(@"Blowed up vehicle {0} to maximum air pressure {1}" + System.Environment.NewLine, VehicleLicenseNumber, ResultVehicleInGarage.Vehicle.ListOfWheels[0].MaxAirPressure);
                System.Threading.Thread.Sleep(2000);
            }
            else
            {
                throw new VehicleNotFoundException("Vehicle " + VehicleLicenseNumber + " not found");
            }
        }
        catch (VehicleNotFoundException e)
        {
            printException(e);
        }
        catch (ArgumentException e)
        {
            printException(e);
        }
        catch (FormatException e)
        {
            printException(e);
        }
        catch (ValueOutOfRangeException e)
        {
            printException(e);
        }
    }

    private static string getLicenseNumberFromUser()
    {
        System.Console.Clear();
        System.Console.WriteLine(@"Please enter the licence number of the desired vehicle: " + System.Environment.NewLine);
        return System.Console.ReadLine();
    }

    public static void ChangeHandledVehicleStatus()
    {
        VehicleInGarage ResultVehicleInGarage = null;

        string VehicleLicenseNumber = getLicenseNumberFromUser();

        try
        {
            if (m_GarageLogic.IsVehicleInGarage(VehicleLicenseNumber, ref ResultVehicleInGarage))
            {
                printChangeHandleVehicleStatusMenu();
                VehicleInGarage.eHandleStatus StatusToChange = getChosenVehicleStatus();
                ResultVehicleInGarage.CurrentHandleStatus = StatusToChange;
                System.Console.Clear();
                System.Console.WriteLine(@"Changed the status of vehicle {0} to {1} " + System.Environment.NewLine, VehicleLicenseNumber, StatusToChange.ToString());
                System.Threading.Thread.Sleep(2000);
            }
            else
            {
                throw new VehicleNotFoundException("Vehicle " + VehicleLicenseNumber + " not found");
            }
        }
        catch (VehicleNotFoundException e)
        {
            printException(e);
        }
        catch (ArgumentException e)
        {
            printException(e);
        }
        catch (FormatException e)
        {
            printException(e);
        }
        catch (ValueOutOfRangeException e)
        {
            printException(e);
        }
    }

    private static void printChangeHandleVehicleStatusMenu()
    {
        string[] HandledVehicleStatusOptions = m_GarageLogic.GetGarageVehicleStatusOptions();

        System.Console.WriteLine("Please enter the desired status:" + System.Environment.NewLine);

        for (int CurrentHandleVehicleStatusOption = 1; CurrentHandleVehicleStatusOption <= HandledVehicleStatusOptions.Length; CurrentHandleVehicleStatusOption++)
        {
            System.Console.WriteLine("{0}. {1}" + System.Environment.NewLine, CurrentHandleVehicleStatusOption, HandledVehicleStatusOptions[CurrentHandleVehicleStatusOption - 1]);
        }
    }

    private static void showLicenseNumberOfAllVehiclesInGarageMenu()
    {
        bool FinishedShowLicenseNumberOfAllVehiclesInGarage = false;

        while (!FinishedShowLicenseNumberOfAllVehiclesInGarage)
        {
            if (m_GarageLogic.VehiclesInGarage.Count.Equals(k_Zero))
            {
                System.Console.Clear();
                System.Console.WriteLine(@"There are no vehicles in garage at the moment." + System.Environment.NewLine);
                System.Threading.Thread.Sleep(2000);
                break;
            }

            try
            {
                int? ChosenUserFilter = getUserChosenVehicleHandleStatusOptions();
                
                List<string> ListOfRequestVehiclesInGarage = m_GarageLogic.GetVehicleInGarageByFilter(ChosenUserFilter);
                printListOfVehicleInGarage(ListOfRequestVehiclesInGarage, ChosenUserFilter);

                FinishedShowLicenseNumberOfAllVehiclesInGarage = true;
            }
            catch (ArgumentException e)
            {
                printException(e);
            }
            catch (FormatException e)
            {
                printException(e);
            }
            catch (ValueOutOfRangeException e)
            {
                printException(e);
            }
        }
    }

    private static void printListOfVehicleInGarage(List<string> ListOfRequestVehiclesInGarage, int? i_Filter)
    {
        if (i_Filter == null)
        {
            System.Console.WriteLine("This is the total list of {0} vehicles in garage:" + System.Environment.NewLine, ListOfRequestVehiclesInGarage.Count);
        }
        else
        {
            System.Console.WriteLine("This is the list {0} vehicles in garage by handle status filter: {1}" + System.Environment.NewLine, ListOfRequestVehiclesInGarage.Count, ((VehicleInGarage.eHandleStatus)i_Filter.Value).ToString());
        }

        int VehicleInGarageIndex = 1;
        foreach (string CurrentVehicleInGarage in ListOfRequestVehiclesInGarage)
        {
            System.Console.WriteLine("{0}. {1}" + System.Environment.NewLine, VehicleInGarageIndex, CurrentVehicleInGarage);
            VehicleInGarageIndex++;
        }

        System.Threading.Thread.Sleep(2000);
    }

    private static int? getUserChosenVehicleHandleStatusOptions()
    {
        int? result = null;
        System.Console.Clear();
        System.Console.WriteLine("Please choose a filter from below:" + System.Environment.NewLine);

        string[] VehicleHandleStatusOptionsArr = m_GarageLogic.GetGarageVehicleStatusOptions();

        for (int CurrentVehicleHandleStatusOption = 0; CurrentVehicleHandleStatusOption < VehicleHandleStatusOptionsArr.Length; CurrentVehicleHandleStatusOption++)
        {
            System.Console.WriteLine("{0}. {1}" + System.Environment.NewLine, CurrentVehicleHandleStatusOption + 1, VehicleHandleStatusOptionsArr[CurrentVehicleHandleStatusOption]);
        }

        System.Console.WriteLine("{0}. No filter" + System.Environment.NewLine, VehicleHandleStatusOptionsArr.Length + 1);

        string ChosenFilterStr = System.Console.ReadLine();
        int ChosenFilterValue = int.Parse(ChosenFilterStr);
        ChosenFilterValue--;

        if (ChosenFilterValue >= 0 && ChosenFilterValue < VehicleHandleStatusOptionsArr.Length)
        {
            result = ChosenFilterValue;
        }

        return result;
    }

    private static void addVehicleToGarageMenu()
    {
        string VehicleLicenseNumber = getLicenseNumberFromUser();
        VehicleInGarage VehicleInGarage = null;

        if (m_GarageLogic.IsVehicleInGarage(VehicleLicenseNumber, ref VehicleInGarage))
        {
            System.Console.WriteLine(@"The vehicle: {0} is already in garage, garage will now repair it. " + System.Environment.NewLine, VehicleLicenseNumber);
            System.Threading.Thread.Sleep(2000);
            VehicleInGarage.CurrentHandleStatus = VehicleInGarage.eHandleStatus.InRepair;
        }
        else
        {
            addNewVehicleToGarageMenu(VehicleLicenseNumber);
        }
    }

    private static void printException(Exception i_Exception)
    {
        System.Console.Clear();
        System.Console.WriteLine(i_Exception.Message);
        System.Threading.Thread.Sleep(2000);
    }

    private static void addNewVehicleToGarageMenu(string i_VehicleLicenseNumber)
    {
        string NewVehicleOwnerName = string.Empty;
        string NewVehicleOwnerPhoneNumber = string.Empty;

        Vehicle NewVehicle = chooseVehicleType();
        NewVehicle.LicenseNumber = i_VehicleLicenseNumber;

        System.Console.WriteLine(@"Please enter new vehicle's owner name: " + System.Environment.NewLine);
        NewVehicleOwnerName = System.Console.ReadLine();
        System.Console.WriteLine(@"Please enter new vehicle's owner phone number: " + System.Environment.NewLine);
        NewVehicleOwnerPhoneNumber = System.Console.ReadLine();

        VehicleInGarage NewVehicleInGarage = new VehicleInGarage(NewVehicle, NewVehicleOwnerName, NewVehicleOwnerPhoneNumber);
        
        foreach (VehicleProperty CurrentVehicleProperty in NewVehicle.VehicleProperties)
        {
            handleCurrentVehicleProperty(NewVehicle, CurrentVehicleProperty);
        }

        m_GarageLogic.VehiclesInGarage.Add(NewVehicleInGarage.Vehicle.LicenseNumber, NewVehicleInGarage);
    }

    private static void handleCurrentVehicleProperty(Vehicle i_HandledVehicle, VehicleProperty i_CurrentVehicleProperty)
    {
        bool FinishedHandleProperty = false;

        while (!FinishedHandleProperty)
        {
            try
            {
                System.Console.Clear();
                System.Console.WriteLine(i_CurrentVehicleProperty.VehiclePropertystring + System.Environment.NewLine);
                i_CurrentVehicleProperty.VehiclePropertyValue = System.Console.ReadLine();
                i_HandledVehicle.SetVehicleProperty(i_CurrentVehicleProperty);
                FinishedHandleProperty = true;
            }
            catch (ArgumentException e)
            {
                printException(e);
            }
            catch (FormatException e)
            {
                printException(e);
            }
            catch (ValueOutOfRangeException e)
            {
                printException(e);
            }
        }
    }

    private static Vehicle chooseVehicleType()
    {
        bool ChooseVehicleTypeEnd = false;
        Vehicle NewVehicle = null;

        while (!ChooseVehicleTypeEnd)
        {
            try
            {
                printAddNewVehicleGarageMenu();
                GarageVehicleAllocator.eVehicleType VehicleType = getChosenVehicleType();
                NewVehicle = GarageVehicleAllocator.AllocateVehicle(VehicleType);
                ChooseVehicleTypeEnd = true;
            }
            catch (ArgumentException e)
            {
                printException(e);
            }
            catch (FormatException e)
            {
                printException(e);
            }
            catch (ValueOutOfRangeException e)
            {
                printException(e);
            }
        }

        return NewVehicle;
    }

    private static GarageVehicleAllocator.eVehicleType getChosenVehicleType()
    {
        m_UserChosenOptionFromUI = System.Console.ReadLine();
        int ChosenVehicleTypeValue;
        GarageVehicleAllocator.eVehicleType? ChosenVehicleType = null;

        try
        {
            ChosenVehicleTypeValue = int.Parse(m_UserChosenOptionFromUI);
            ChosenVehicleTypeValue--;
            m_GarageLogic.EnumRangeValidator(typeof(GarageVehicleAllocator.eVehicleType), ChosenVehicleTypeValue);
            ChosenVehicleType = (GarageVehicleAllocator.eVehicleType)ChosenVehicleTypeValue;
        }
        catch (FormatException e)
        {
            throw new ValueOutOfRangeException("Invalid menu option chosen", e);
        }

        return ChosenVehicleType.Value;
    }

    private static eMenuOption getUserChosenOption()
    {
        m_UserChosenOptionFromUI = System.Console.ReadLine();
        int ChosenUserOptionValue;
        eMenuOption? UserChosenMenuOption = null;

        try
        {
            ChosenUserOptionValue = int.Parse(m_UserChosenOptionFromUI);

            ChosenUserOptionValue--;
            m_GarageLogic.EnumRangeValidator(typeof(eMenuOption), ChosenUserOptionValue);
            UserChosenMenuOption = (eMenuOption)ChosenUserOptionValue;

            return UserChosenMenuOption.Value;
        }
        catch (FormatException e)
        {
            throw new ValueOutOfRangeException("Invalid menu option chosen", e);
        }
    }

    private static VehicleInGarage.eHandleStatus getChosenVehicleStatus()
    {
        m_UserChosenOptionFromUI = System.Console.ReadLine();
        int ChosenVehicleStatusValue;
        VehicleInGarage.eHandleStatus? ChosenVehicleStatus = null;

        try
        {
            ChosenVehicleStatusValue = int.Parse(m_UserChosenOptionFromUI);
            ChosenVehicleStatusValue--;
            m_GarageLogic.EnumRangeValidator(typeof(VehicleInGarage.eHandleStatus), ChosenVehicleStatusValue);
            ChosenVehicleStatus = (VehicleInGarage.eHandleStatus)ChosenVehicleStatusValue;

            return ChosenVehicleStatus.Value;
        }
        catch (FormatException e)
        {
            throw new ValueOutOfRangeException("Invalid menu option chosen", e);
        }
    }

    private static FuelEngine.eEngineFuelType getChosenFuelType()
    {
        printFuelTypesOptions();

        m_UserChosenOptionFromUI = System.Console.ReadLine();
        int InputChosenUserFuelType;
        FuelEngine.eEngineFuelType? ChosenVehicleStatus = null;

        try
        {
            InputChosenUserFuelType = int.Parse(m_UserChosenOptionFromUI);
            InputChosenUserFuelType--;
            m_GarageLogic.EnumRangeValidator(typeof(FuelEngine.eEngineFuelType), InputChosenUserFuelType);
            ChosenVehicleStatus = (FuelEngine.eEngineFuelType)InputChosenUserFuelType;

            return ChosenVehicleStatus.Value;
        }
        catch (FormatException e)
        {
            throw new ValueOutOfRangeException("Invalid menu option chosen", e);
        }
    }

    private static void printFuelTypesOptions()
    {
        string[] FuelTypesOptions = Enum.GetNames(typeof(FuelEngine.eEngineFuelType));

        System.Console.WriteLine(@"Please choose the desired fuel type:" + System.Environment.NewLine);

        for (int i = 1; i <= FuelTypesOptions.Length; i++)
        {
            System.Console.WriteLine("{0}. {1}" + System.Environment.NewLine, i, FuelTypesOptions[i - 1]);    
        }
    }

    private static void printAddNewVehicleGarageMenu()
    {
        System.Console.Clear();
        System.Console.WriteLine(@"Please choose vehicle type from options below: " + System.Environment.NewLine);
        List<string> VehicleTypesList = GarageVehicleAllocator.GetListOfVehicleTypes();

        for (int i = 1; i <= VehicleTypesList.Count; i++)
        {
            System.Console.WriteLine("{0}. {1}" + System.Environment.NewLine, i, VehicleTypesList[i - 1]);
        }
    }

    private static void printHeader()
    {
        System.Console.WriteLine(@"*******************************************************************************" + System.Environment.NewLine);
    }
}